/*
 * File: vfsdriver.c
 *
 * Description: This is a driver program for testing your VFS system using an interaction script as input
 * You need to make additional calls to your respective functions as noted in the comments below
 * Make sure the output you display is exactly as per the given specifications for you. Do NOT print
 * any extra output (like debug messages) in the final version of your driver program. You can use this driver program
 * in a in incremental manner as you keep implementing one operator after another. For operators not yet implemented,
 * you can leave the output as given ("TO_BE_DONE"). So you can use this program as your "main" program for testing purposes.
 *
 * DO NOT write the full code for operators in the driver program! You must only CALL your functions from here.
 *
 * Usage: vfsdriver <scriptfilename>
 */

# include <stdlib.h>
# include <stdio.h>
# include <string.h>
# include "../include/CreateVFS.h"
# include "../include/Header.h"
# include "../include/NArrayTree.h"
# include "../include/MountVFS.h"
# include "../include/Hashing.h"
# include "../include/BinarySearchTree.h"
# include "../include/DirectoryOps.h"
# include "../include/vfs_errorcodes.h"

#define BUFSIZE 200
#define CMDSIZE 30
#define PARSIZE 100

struct VFS *mainHeader;
struct NArray_node *NArrayRoot = '\0';
struct BST_node *BSTRoot = '\0';
struct HashNode **table;

void createvfs ( char *P1, int P2 );
void mountvfs ( char *P1 );
void unmountvfs ( char *P1 );
void makedir ( char *P1, char *P2 );
void deletedir ( char *P1 );
void movedir ( char *P1, char *P2 );
void listdir ( char *P1, int P2, char *P3 );
void addfile ( char *P1, char *P2, char *P3 );
void listfile ( char *P1, char *P2 );
void updatefile ( char *P1, char *P2 );
void removefile ( char *P1 );
void movefile ( char *P1, char *P2 );
void copyfile ( char *P1, char *P2 );
void exportfile ( char *P1, char *P2 );
void searchfile ( char *P1, char *P2 );

void processcommand( char *command, char *P1, char *P2, char *P3 );

int main( int argc, char *argv[] )
{
	FILE *scriptfp;
	char linebuffer[BUFSIZE];
	char command[CMDSIZE], par1[PARSIZE], par2[PARSIZE], par3[PARSIZE];
	char *token;

	if( argc != 2 ){
		fprintf(stderr,"Usage: vfsdriver <scriptfile>\n");
		return(1);
	}

	if( (scriptfp=(FILE *)fopen(argv[1],"r")) == NULL ){
		fprintf(stderr,"Unable to open script file: %s\n", argv[1]);
		return(2);
	}

	while( fgets(linebuffer, sizeof(linebuffer), scriptfp) != NULL ){
		/* This output is for debugging... do not uncomment in final version */
		/*
		printf("==================================================\n");
		printf("Processing: %s", linebuffer);
		printf("==================================================\n");
		*/

		/* Remove the extra newline character in the end of line */
		linebuffer[ strlen(linebuffer)-1 ] = '\0';
		
		/* Get the command and the parameters using tokenizer */
		strcpy( command, (token = strtok(linebuffer, " ")) == NULL ? "" : token );
		
		strcpy( par1, (token = strtok(NULL, " ")) == NULL ? "" : token );
		strcpy( par2, (token = strtok(NULL, " ")) == NULL ? "" : token );
		strcpy( par3, (token = strtok(NULL, " ")) == NULL ? "" : token );
		/* printf("Command:%s:p1:%s:p2:%s:p3:%s\n",command, par1, par2, par3); */

		processcommand( command, par1, par2, par3 );
	}
	fclose(scriptfp);
}

void processcommand( char *command, char *P1, char *P2, char *P3 )
{
	if( strcmp(command, "createvfs") == 0 ){
		int size = atoi(P2);
		if (*(P1) != '\0' && *(P2)) 
			createvfs (P1,size);
		else {
			printf("createvfs_FAILURE ");
			puts(ERR_VFS_CREATE_00);
		}
	}
	else if( strcmp(command, "mountvfs") == 0 ) {
		if (*(P1) != '\0') 
			mountvfs (P1);
		else {
			printf("mountvfs_FAILURE ");
			puts(ERR_VFS_MOUNT_00);
		}
	}
	else if( strcmp(command, "unmountvfs") == 0 ) {
		if (*(P1) != '\0') 
			unmountvfs (P1);
		else {
			printf("unmountvfs_FAILURE ");
			puts(ERR_VFS_UNMOUNT_00);
		}
	}
	else if( strcmp(command, "makedir") == 0 ) {
		if (*(P1) != '\0' && *(P2) != '\0')
			makedir (P1,P2);
		else {
			printf("makedir_FAILURE ");
			puts(ERR_VFS_MAKEDIR_00);
		}
	}
	else if( strcmp(command, "deletedir") == 0 ) {
		if (*(P1) != '\0')
			deletedir (P1);
		else {
			printf("deletedir_FAILURE ");
			puts(ERR_VFS_DELETEDIR_00);
		}
	}
	else if( strcmp(command, "movedir") == 0 ) {
		if (*(P1) != '\0' && *(P2) != '\0')
			movedir (P1,P2);
		else {
			printf("movedir_FAILURE ");
			puts(ERR_VFS_MOVEDIR_00);
		}
	}
	else if( strcmp(command, "listdir") == 0 ){
		int flag = atoi(P2);
		if (*(P1) != '\0' && flag >= 0 && *(P3) != '\0')
			listdir (P1,flag,P3);
		else {
			printf("listdir_FAILURE ");
			puts(ERR_VFS_LISTDIR_00);
		}
	}
	else if( strcmp(command, "addfile") == 0 ) {
		if (*(P1) != '\0' && *(P2) != '\0' && *(P3) != '\0')
			addfile (P1,P2,P3);
		else {
			printf("addfile_FAILURE ");
			puts(ERR_VFS_ADDFILE_00);
		}
	}
	else if( strcmp(command, "listfile") == 0 ) {
		if (*(P1) != '\0' && *(P2) != '\0')
			listfile (P1,P2);
		else {
			printf("listfile_FAILURE ");
			puts(ERR_VFS_LISTFILE_00);
		}
	}
	else if( strcmp(command, "updatefile") == 0 ) {
		if (*(P1) != '\0' && *(P2) != '\0')
			updatefile (P1,P2);
		else {
			printf("updatefile_FAILURE ");
			puts(ERR_VFS_UPDATEFILE_00);
		}
	}
	else if( strcmp(command, "removefile") == 0 ) {
		if (*(P1) != '\0')
			removefile (P1);
		else {
			printf("removefile_FAILURE ");
			puts(ERR_VFS_REMOVEFILE_00);
		}
	}
	else if( strcmp(command, "movefile") == 0 ) {
		if (*(P1) != '\0' && *(P2) != '\0')
			movefile (P1,P2);
		else {
			printf("movefile_FAILURE ");
			puts(ERR_VFS_MOVEFILE_00);
		}
	}
	else if( strcmp(command, "copyfile") == 0 ) {
		if (*(P1) != '\0' && *(P2) != '\0')
			copyfile (P1,P2);
		else {
			printf("copyfile_FAILURE ");
			puts(ERR_VFS_COPYFILE_00);
		}
	}
	else if( strcmp(command, "exportfile") == 0 ) {
		if (*(P1) != '\0' && *(P2) != '\0')
			exportfile (P1,P2);
		else {
			printf("exportfile_FAILURE ");
			puts(ERR_VFS_EXPORTFILE_00);
		}
	}
	else if( strcmp(command, "searchfile") == 0 ) {
		if (*(P1) != '\0' && *(P2) != '\0')
			searchfile (P1,P2);
		else {
			printf("searchfile_FAILURE ");
			puts(ERR_VFS_SEARCHFILE_00);
		}
	}
	else
		printf("Ignoring invalid command %s\n", command);
}

void createvfs ( char *P1, int P2 )
{
	int ret = 0;
	if(!validateFileName (P1)) {
		printf("createvfs_FAILURE ");
		puts(ERR_VFS_CREATE_03);
		return;
	}
	if (!validateSize (P2))	{
		printf("createvfs_FAILURE ");
		puts(ERR_VFS_CREATE_04);
		return;
	}
	if (stringLength(P1) > 30) {
		printf("createvfs_FAILURE ");
		puts(ERR_VFS_CREATE_05);
		return;
	}
	if (checkFile(P1)) {
		printf("createvfs_FAILURE ");
		puts(ERR_VFS_CREATE_01);
		return;
	}
	mainHeader = create_block(P1);
	if (loadFile("input1.txt") != '\0')
		read_block(mainHeader, "input1.txt");
	ret = unmount_vfs(mainHeader, P1);

	/*if (ret == 2) {
		puts(ERR_VFS_UNMOUNT_01);
	}*/

	if (ret == 0) {
		freeNArrayNode (NArrayRoot);
		freeBSTNode (BSTRoot);
		free (table);
		NArrayRoot = '\0';
		BSTRoot = '\0';
		table = '\0';
		puts("createvfs_SUCCESS");
	}
	else if (ret == 1) {
		printf("createvfs_FAILURE ");
		puts(ERR_VFS_CREATE_01);
	}
	else if (ret == 2) {
		printf("createvfs_FAILURE ");
		puts(ERR_VFS_CREATE_02);
	}
	free(mainHeader);
	mainHeader = '\0';
}

void mountvfs ( char *P1 )
{
	if (mainHeader != '\0') {
		printf("mountvfs_FAILURE ");
		puts(ERR_VFS_MOUNT_03);
		return;
	}
	mainHeader = create_block(P1);
	mainHeader = mount_vfs(mainHeader, P1);
	if (mainHeader) {
		NArrayRoot = mountNArray (mainHeader, NArrayRoot);
		BSTRoot = mountBST (mainHeader, BSTRoot);
		table = mountHashTable (mainHeader, table);
		NArrayRoot = insertNArrayNode(NArrayRoot, "/root");
		BSTRoot = insertBSTNode(BSTRoot, "/root");
		puts("mountvfs_SUCCESS");
	}
	else {
		free(mainHeader);
		mainHeader = '\0';
	}
}

void unmountvfs ( char *P1 )
{
	int ret = 0;
	if (mainHeader == '\0') {
		printf("unmountvfs_FAILURE ");
		puts(ERR_VFS_UNMOUNT_03);
		return;
	}
	if (!checkFile(P1)) {
		printf("unmountvfs_FAILURE ");
		puts(ERR_VFS_CREATE_01);
		return;
	}
	ret = unmount_vfs(mainHeader, P1);
	if (ret == 0) {
		freeNArrayNode (NArrayRoot);
		freeBSTNode (BSTRoot);
		free (table);
		NArrayRoot = '\0';
		BSTRoot = '\0';
		table = '\0';
		free(mainHeader);
		mainHeader = '\0';
		puts("unmountvfs_SUCCESS");
	}
	else if (ret == 2) {
		printf("unmountvfs_FAILURE ");
		puts(ERR_VFS_UNMOUNT_02);
	}
}

void makedir ( char *P1, char *P2 )
{
	int l;
	char str1[100], str2[50];
	int ret = 0;
	if (mainHeader == '\0') {
		printf("makedir_FAILURE ");
		puts(ERR_VFS_MAKEDIR_05);
		return;
	}
	if(!validateFileName (P2)) {
		printf("makedir_FAILURE ");
		puts(ERR_VFS_MAKEDIR_02);
		return;
	}
	for (l = 0; l < MAX_FD_SIZE; l++)
		if(mainHeader -> freeList[l] == 0)
			break;
	if (l == MAX_FD_SIZE) {
		printf("makedir_FAILURE ");
		puts(ERR_VFS_MAKEDIR_01);
		return;
	}
	copyString (str1, P1);
	copyString (str2, P2);
	validateDirPath (str1, str2);
	if (searchNArrayNode (NArrayRoot, strcat(str1, str2))) {
		printf("makedir_FAILURE ");
		puts(ERR_VFS_MAKEDIR_03); //"DIRECTORY_ALREADY_EXISTS"
		return;
	}
	makeDir(P1, P2, mainHeader);
	NArrayRoot = insertNArrayNode(NArrayRoot, P1);
	BSTRoot = insertBSTNode(BSTRoot, P1);
	puts("makedir_SUCCESS");
}

void deletedir ( char *P1 )
{
	struct NArray_node *temp;
	if (mainHeader == '\0') {
		printf("deletedir_FAILURE ");
		puts(ERR_VFS_DELETEDIR_04);
		return;
	}
	temp = searchNArrayNode (NArrayRoot, P1);
	if (!temp) {
		printf("deletedir_FAILURE ");
		puts(ERR_VFS_DELETEDIR_01);
		return;
	}
	if (temp -> leftChild) {
		printf("deletedir_FAILURE ");
		puts(ERR_VFS_DELETEDIR_02);
		return;
	}
	deleteDir(P1, mainHeader);
	makeDir(truncateRight(P1), "/", mainHeader);
	freeNArrayNode (NArrayRoot);
	freeBSTNode (BSTRoot);
	free (table);
	NArrayRoot = '\0';
	BSTRoot = '\0';
	table = '\0';
	NArrayRoot = mountNArray (mainHeader, NArrayRoot);
	BSTRoot = mountBST (mainHeader, BSTRoot);
	table = mountHashTable (mainHeader, table);					
	//printf("Directory structure updated\n");
	//displayNArray(NArrayRoot);
	puts("deletedir_SUCCESS");
}

void movedir ( char *P1, char *P2 )
{
	int j, ret;
	char source[100], temp[100],temp1[100], temp2[100], **data = NULL, dest[50];
	if (mainHeader == '\0') {
		printf("movedir_FAILURE ");
		puts(ERR_VFS_MOVEDIR_08);
		return;
	}
	copyString (temp, P1);
	if (!searchNArrayNode (NArrayRoot, temp)) {
		printf("movedir_FAILURE ");
		puts(ERR_VFS_MOVEDIR_01); 
		return;
	}
	copyString (temp2, P2);
	if (!searchNArrayNode (NArrayRoot, temp2)) {
		printf("movedir_FAILURE ");
		puts(ERR_VFS_MOVEDIR_02); //"CANNOT_FIND_SPECIFIED_DESTINATIONDIR"
		return;
	}
	data = listDir(checkRoot(P2), 0, NArrayRoot);
	copyString(dest, getLastDir(temp));
	for (j = 0; (data + j) && *(data + j)[0] != ' '; j++) {
		if (!strcmp(*(data + j), dest)) {
		printf("movedir_FAILURE ");
			puts(ERR_VFS_MOVEDIR_05);
			return;
		}
	}
	/*copyString (temp1, P1);
	/*copyString (temp2, P1);
	while (temp1) {
		copyString(source, split (temp1));
		//coptemp1, truncateLeft(temp1, stringLength(source));
		copyString(temp1, truncateLeft(temp1, stringLength(source)));
	}*/
	/*j = stringLength(temp1);
	if ( *(temp1 + j - 1) != '/')
		while( *(temp1 + j - 1) != '/' ) {
			j--;
		}
	else {
		j--;
		while( *(temp1 + j - 1) != '/' ) {
			j--;
		}
	}
	copyString (source, temp1 + j);
	validateDirPath(temp2, source);
	if (searchNArrayNode (NArrayRoot, strcat(temp2, source))) {
		puts(ERR_VFS_MOVEDIR_05);///////////
		return;
	}*/
	ret = moveDirectory (checkRoot(P1), checkRoot(P2), mainHeader);
	if (ret == 0) {
		freeNArrayNode (NArrayRoot);
		freeBSTNode (BSTRoot);
		free (table);
		NArrayRoot = '\0';
		BSTRoot = '\0';
		table = '\0';
		NArrayRoot = mountNArray (mainHeader, NArrayRoot);
		BSTRoot = mountBST (mainHeader, BSTRoot);
		table = mountHashTable (mainHeader, table);
		puts("movedir_SUCCESS");			
	}
	else if (ret == 4) {
		printf("movedir_FAILURE ");
		puts(ERR_VFS_MOVEDIR_04);
	}
	else if (ret == 6) {
		printf("movedir_FAILURE ");
		puts(ERR_VFS_MOVEDIR_06);
	}
	else if (ret == 7) {
		printf("movedir_FAILURE ");
		puts(ERR_VFS_MOVEDIR_07);
	}
	else {
		printf("movedir_FAILURE ");
		puts(ERR_VFS_MOVEDIR_01);
	}
}

void listdir ( char *P1, int P2, char *P3 )
{
	int res	= 0;
	char **list;
	if (mainHeader == '\0') {
		printf("listdir_FAILURE ");
		puts(ERR_VFS_LISTDIR_03);
		return;
	}
	if (P2 != 0 && P2 != 1) {
		printf("listdir_FAILURE ");
		puts(ERR_VFS_LISTDIR_02);
		return;
	}
	list = listDir(checkRoot(P1), P2, NArrayRoot);
	if (list) {
		if(writeFile(P3, list)){
			printf("listdir_FAILURE ");
			puts(ERR_VFS_LISTDIR_04);
		}
		else {
		
			puts("listdir_SUCCESS");
		}
	}
	else {
		printf("listdir_FAILURE ");
		puts(ERR_VFS_LISTDIR_01);
		return;
	}
	//displayNArray(NArrayRoot);
	//displayBST(BSTRoot);
	//displayHashTable(table);
}

void addfile ( char *P1, char *P2, char *P3 )
{
	char *data, temp[100];
	int ret;
	struct HashNode **temp2;
	if (mainHeader == '\0') {
		printf("addfile_FAILURE ");
		puts(ERR_VFS_ADDFILE_07);
		return;
	}
	copyString (temp, P1);
	validateDirPath(temp, P2);
	if (searchNArrayNode (NArrayRoot, strcat(temp,P2))) {
		printf("addfile_FAILURE ");
		puts(ERR_VFS_ADDFILE_03); 
		return;
	}
	if(!validateFileName (P2)) {
		printf("addfile_FAILURE ");
		puts(ERR_VFS_ADDFILE_02);
		return;
	}
	data = loadFile(P3);
	if (data == '\0') {
		puts("Data file not found");
		return;
	}
	if (stringLength(data) > 1024) {
		printf("addfile_FAILURE ");
		puts(ERR_VFS_ADDFILE_06);
		return;
	}
	
	ret = addFile(P1, P2, data, mainHeader, NArrayRoot, BSTRoot, table);
	
	if (ret >=  0) {
		NArrayRoot = insertNArrayNode(NArrayRoot, temp);
		BSTRoot = insertBSTNode(BSTRoot, temp);
		temp2 = hash (mainHeader -> fd[ret].fileName, table);
		*temp2 = addHashNode (&mainHeader -> fd[ret], *temp2);
		puts("addfile_SUCCESS");
	}
	else {
		printf("addfile_FAILURE ");
		puts(ERR_VFS_ADDFILE_04);
	}
	//displayNArray(NArrayRoot);
}

void listfile ( char *P1, char *P2 )
{
	char *data;
	int ret;
	if (mainHeader == '\0') {
		printf("listfile_FAILURE ");
		puts(ERR_VFS_LISTFILE_04);
		return;
	}
	ret = listFile(P1, P2, mainHeader, NArrayRoot);
	if(ret == 1)
		puts("listFile_SUCCESS");
	else if(ret == 0) {
		printf("listfile_FAILURE ");
		puts(ERR_VFS_LISTFILE_01);
	}
	else if(ret == 3) {
		printf("listfile_FAILURE ");
		puts(ERR_VFS_LISTFILE_03);
	}
	else {
		printf("listfile_FAILURE ");
		puts(ERR_VFS_LISTFILE_02);
	}
}

void updatefile ( char *P1, char *P2 )
{
	char *data;
	int ret;
	if (mainHeader == '\0') {
		printf("updatefile_FAILURE ");
		puts(ERR_VFS_UPDATEFILE_04);
		return;
	}
	data = loadFile(P2);
	if (data == '\0') {
		printf("updatefile_FAILURE ");
		puts(ERR_VFS_UPDATEFILE_02);
		return;
	}
	if (stringLength(data) > 1024) {
		printf("updatefile_FAILURE ");
		puts(ERR_VFS_UPDATEFILE_03);
		return;
	}
	ret = updateFile(P1, data, mainHeader);
	if (ret == 1) 
		puts("updatefile_SUCCESS");
	else {
		printf("updatefile_FAILURE ");
		puts(ERR_VFS_UPDATEFILE_01);
	}
}

void removefile ( char *P1 )
{
	if (mainHeader == '\0') {
		printf("removefile_FAILURE ");
		puts(ERR_VFS_REMOVEFILE_02);
		return;
	}
	if (removeFile(P1, mainHeader)) {
		freeNArrayNode (NArrayRoot);
		freeBSTNode (BSTRoot);
		free (table);
		NArrayRoot = '\0';
		BSTRoot = '\0';
		table = '\0';
		NArrayRoot = mountNArray (mainHeader, NArrayRoot);
		BSTRoot = mountBST (mainHeader, BSTRoot);
		table = mountHashTable (mainHeader, table);
		puts("removefile_SUCCESS");
	}
	else {
		printf("removefile_FAILURE ");
		puts(ERR_VFS_REMOVEFILE_01);
	}
}

void movefile ( char *P1, char *P2 )
{
	int ret;
	if (mainHeader == '\0') {
		printf("movefile_FAILURE ");
		puts(ERR_VFS_MOVEFILE_06);
		return;
	}
	ret = moveFile (checkRoot(P1), checkRoot(P2), mainHeader, NArrayRoot);
	if(ret == 2) {
		freeNArrayNode (NArrayRoot);
		freeBSTNode (BSTRoot);
		free (table);
		NArrayRoot = '\0';
		BSTRoot = '\0';
		table = '\0';
		NArrayRoot = mountNArray (mainHeader, NArrayRoot);
		BSTRoot = mountBST (mainHeader, BSTRoot);
		table = mountHashTable (mainHeader, table);
		puts("movefile_SUCCESS");
	}
	else if (ret == 1) {
		printf("movefile_FAILURE ");
		puts(ERR_VFS_MOVEFILE_02);
	}
	else {
		printf("movefile_FAILURE ");
		puts(ERR_VFS_MOVEFILE_01);
	}
}

void copyfile ( char *P1, char *P2 )
{
	int ret;
	if (mainHeader == '\0') {
		printf("copyfile_FAILURE ");
		puts(ERR_VFS_COPYFILE_05);
		return;
	}
	ret = copyFile(checkRoot(P1), checkRoot(P2), mainHeader, NArrayRoot, BSTRoot, table);
	if (ret == 0)
		puts("copyfile_SUCCESS");
	else if (ret == 1) {
		printf("copyfile_FAILURE ");
		puts(ERR_VFS_COPYFILE_01);
	}
	else if (ret == 2) {
		printf("copyfile_FAILURE ");
		puts(ERR_VFS_COPYFILE_02);
	}
	else if (ret == 3) {
		printf("copyfile_FAILURE ");
		puts(ERR_VFS_COPYFILE_03);
	}
	else {
		printf("copyfile_FAILURE ");
		puts(ERR_VFS_COPYFILE_04);
	}
}

void exportfile ( char *P1, char *P2 )
{
	int ret;
	if (mainHeader == '\0') {
		printf("exportfile_FAILURE ");
		puts(ERR_VFS_EXPORTFILE_04);
		return;
	}
	ret = exportFile(checkRoot(P1), P2, mainHeader, NArrayRoot);
	if (ret == 0)
		puts("exportfile_SUCCESS");
	else if (ret == 1) {
		printf("exportfile_FAILURE ");
		puts(ERR_VFS_EXPORTFILE_01);
	}
	else if (ret == 2) {
		printf("exportfile_FAILURE ");
		puts(ERR_VFS_EXPORTFILE_02);
	}
	else {
		printf("exportfile_FAILURE ");
		puts(ERR_VFS_EXPORTFILE_03);
	}
}

void searchfile ( char *P1, char *P2 )
{
	int count;
	char **data;
	if (mainHeader == '\0') {
		printf("searchfile_FAILURE ");
		puts(ERR_VFS_SEARCHFILE_02);
		return;
	}
	data = searchFile (P1, P2, mainHeader);
	count = writeFileLines(P2, data);
	printf("searchfile_SUCCESS %d\n", count);
}
